package com.lzh.hosp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lzh.hosp.model.*;
import com.lzh.hosp.model.dto.LoginDto;
import com.lzh.hosp.service.SysMenuService;
import com.lzh.hosp.service.SysRoleService;
import com.lzh.hosp.service.SysUserService;
import com.lzh.hosp.mapper.SysUserMapper;
import com.lzh.hosp.utils.JwtUtil;
import com.lzh.hosp.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 59399
* @description 针对表【sys_user】的数据库操作Service实现
* @createDate 2023-05-04 22:17:20
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
    implements SysUserService{

    @Autowired
    SysRoleService sysRoleService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    SysUserMapper sysUserMapper;

    @Autowired
    SysMenuService sysMenuService;

    @Autowired
    private RedisCache redisCache;

    @Override
    public ResponseResult login(LoginDto user){
        try {
            SysUser sysUser = this.getByPhone(user.getPhone());
            //判断用户是否锁定
            if (sysUser.getStatu()==0){
                return ResponseResult.fail("用户尚未通过认证，请联系管理员");
            }
            //使用Authentication的实现类
            Authentication authentication = new UsernamePasswordAuthenticationToken(user.getPhone(), user.getPassword());

            //手动调用方法去认证 他会自动调用UserDetailsService查 然后对比
            Authentication authenticate = authenticationManager.authenticate(authentication);
            if(Objects.isNull(authenticate)){ //说明输入错误
                throw new RuntimeException("用户名或密码错误");
            }
            //拿到用户信息 然后生成jwt返回给前端，并且将用户的信息存入redis
            AccountUser loginUser = (AccountUser)authenticate.getPrincipal(); // 这个其实就是UserDetails 也就是LoginUser
            String userId = loginUser.getUserId().toString();

            String jwt = JwtUtil.createJWT(userId);
            redisCache.setCacheObject("login:"+userId,sysUser);//将用户信息直接存入redis

            Map<String, String> map = new HashMap<>();
            map.put("token",jwt);
            map.put("userId",userId);
            map.put("phone", user.getPhone());
            return new ResponseResult(200,map);
        } catch (RuntimeException e) {
            throw e;
        }
    }

    @Override
    public SysUser getByUsername(String username) {
        return getOne(new QueryWrapper<SysUser>().eq("username", username));
    }

    @Override
    public SysUser getByPhone(String phone) {
        return getOne(new QueryWrapper<SysUser>().eq("phone", phone));
    }

    @Override
    public String getUserAuthorityInfo(Long userId) {

        SysUser sysUser = sysUserMapper.selectById(userId);

        //  ROLE_admin,ROLE_normal,sys:user:list,....
        String authority = "";

        if (redisCache.hasKey("GrantedAuthority:" + sysUser.getId())) {
            authority = (String) redisCache.getCacheObject("GrantedAuthority:" + sysUser.getId());

        } else {
            // 获取角色编码
            List<SysRole> roles = sysRoleService.list(new QueryWrapper<SysRole>()
                    .inSql("id", "select role_id from sys_user_role where user_id = " + userId));

            if (roles.size() > 0) {
                String roleCodes = roles.stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
                authority = roleCodes.concat(",");
            }

            // 获取菜单操作编码
            List<Long> menuIds = sysUserMapper.getNavMenuIds(userId);
            if (menuIds.size() > 0) {

                List<SysMenu> menus = sysMenuService.listByIds(menuIds);
                String menuPerms = menus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));

                authority = authority.concat(menuPerms);
            }

            redisCache.setCacheObject("GrantedAuthority:" + sysUser.getId(), authority, 60 * 60, TimeUnit.SECONDS);
        }

        return authority;
    }

    @Override
    public void clearUserAuthorityInfo(Long userId) {
        redisCache.deleteObject("GrantedAuthority:" + userId);
    }

    @Override
    public void clearUserAuthorityInfoByRoleId(Long roleId) {

        List<SysUser> sysUsers = this.list(new QueryWrapper<SysUser>()
                .inSql("id", "select user_id from sys_user_role where role_id = " + roleId));

        sysUsers.forEach(u -> {
            this.clearUserAuthorityInfo(u.getId());
        });

    }

    @Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
        List<SysUser> sysUsers = sysUserMapper.listByMenuId(menuId);

        sysUsers.forEach(u -> {
            this.clearUserAuthorityInfo(u.getId());
        });
    }

    @Override
    public ResponseResult phoneLogin(LoginDto loginDto) {
        SysUser sysUser = this.getByPhone(loginDto.getPhone());
        //判断用户是否锁定
        if (sysUser.getStatu()==0){
            return ResponseResult.fail("用户尚未通过认证，请联系管理员");
        }

        //判读验证码
        String code = (String)redisCache.getCacheObject(loginDto.getPhone());
        if (StringUtils.isEmpty(code)){
            return ResponseResult.fail("验证码不存在，请重新获取");
        }
        if (!loginDto.getCode().equals(code)){
            return ResponseResult.fail("验证码错误，请重新填写");
        }

        //使用Authentication的实现类
        Authentication authentication = new UsernamePasswordAuthenticationToken(sysUser.getPhone(), sysUser.getPassword());

        //手动调用方法去认证 他会自动调用UserDetailsService查 然后对比
        Authentication authenticate = authenticationManager.authenticate(authentication);
        if(Objects.isNull(authenticate)){ //说明输入错误
            throw new RuntimeException("用户名或密码错误");
        }
        //拿到用户信息 然后生成jwt返回给前端，并且将用户的信息存入redis
        AccountUser loginUser = (AccountUser)authenticate.getPrincipal(); // 这个其实就是UserDetails 也就是LoginUser
        String userId = loginUser.getUserId().toString();

        String jwt = JwtUtil.createJWT(userId);
        redisCache.setCacheObject("login:"+userId,sysUser);//将用户信息直接存入redis

        Map<String, String> map = new HashMap<>();
        map.put("token",jwt);
        map.put("userId",userId);
        map.put("phone", sysUser.getPhone());
        return new ResponseResult(200,map);
    }
}




